package com.imarad.bio.socket.core;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.util.Log;


import com.imarad.bio.socket.common.ClientPacket;
import com.imarad.bio.socket.common.ClientUtils;
import com.imarad.bio.socket.common.Constants;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * Copyright2017-2017  AMBITION.All Rights Reserved
 *
 * Comments：
 *
 * @author huh
 *
 *         Time: 2018/2/1
 *
 *         Modified By:
 *         Modified Date:
 *         Why & What is modified:
 * @version 1.0.0
 */
class SocketManager {

    private static final String TAG = "SocketManager";

    /**
     * 网路套接字
     */
    private Socket mSocket;

    /**
     * 连接回调
     */
    private ConnectedCallback mConnectedCallback;

    /**
     * 读线程
     */
    private ReadSocketClient mReadSocketClient;

    /**
     * 写线程
     */
    private WriteSocketClient mWriteSocketClient;

    /**
     * socket 属性
     */
    private SocketProperty mSocketProperty;

    public SocketManager(SocketProperty socketProperty,
            ConnectedCallback connectedCallback) {
        mSocketProperty = socketProperty;
        mConnectedCallback = connectedCallback;
        new Thread() {
            @Override
            public void run() {
                initSocket();
            }
        }.start();
    }

    /**
     * 初始化socket
     */
    private void initSocket() {
        if (mConnectedCallback == null) {
            throw new SocketClientException("null ConnectedCallback");
        }
        //判断网络类型是否有初始值
        if (mSocketProperty.getNetType() == 0) {
            //根据请求的ip地址来判断
            if (ClientUtils.isInternet(mSocketProperty.getHost())) {
                mSocketProperty.setNetType(Constants.INTERNET);
            } else {
                mSocketProperty.setNetType(Constants.LOCAL_AREA_NETWORK);
            }
        }
        try {
            //网络类型为internet，启动4G；反之都走wifi网络
            if (mSocketProperty.getNetType() == Constants.INTERNET) {
                initMobileSocket();
            } else {
                initWifiSocket();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //服务器连接失败
            mConnectedCallback.connectFail("服务器连接失败:" + e.getLocalizedMessage());
            stopClient();
        }
    }

    /**
     * 连接wifi
     *
     * @throws IOException 网络连接异常
     */
    private void initWifiSocket() throws IOException {
        mSocket = new Socket();
        mSocket.setTcpNoDelay(true);
        Log.d(TAG, "Wifi网络连接[" + mSocketProperty.getHost() + ":" + mSocketProperty.getPort() + "]");
        mSocket.connect(new InetSocketAddress(mSocketProperty.getHost(), mSocketProperty.getPort()),
                1000);
        connectSuccess();
    }

    /**
     * 连接移动网络
     *
     * @throws IOException 网络连接异常
     */
    private void initMobileSocket() throws IOException {
        final ConnectivityManager connectivityManager = (ConnectivityManager) mSocketProperty
                .getContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkRequest.Builder builder = new NetworkRequest.Builder();

        // 设置指定的网络传输类型(蜂窝传输) 等于手机网络
        builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
        //移动网络
        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        //计费网络builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);

        NetworkRequest request = builder.build();
        ConnectivityManager.NetworkCallback callback
                = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                try {
                    mSocket = new Socket();
                    network.bindSocket(mSocket);
                    mSocket.setTcpNoDelay(mSocketProperty.getTcpNoDelay());
                    Log.d(TAG,
                            "移动网络连接[" + mSocketProperty.getHost() + ":" + mSocketProperty.getPort()
                                    + "]");
                    mSocket.connect(new InetSocketAddress(mSocketProperty.getHost(),
                            mSocketProperty.getPort()), mSocketProperty.getTimeOuts());
                } catch (IOException e1) {
                    e1.printStackTrace();
                    mConnectedCallback.connectFail("服务器连接失败:" + e1.getLocalizedMessage());
                }
                connectSuccess();
                //发现更优质网络注销掉connectivityManager.unregisterNetworkCallback(this);
            }
        };
        connectivityManager.requestNetwork(request, callback);
    }

    /**
     * 网络连接成功操作
     */
    private void connectSuccess() {
        mWriteSocketClient = new WriteSocketClient(mSocket, mConnectedCallback);
        mReadSocketClient = new ReadSocketClient(mSocket, mConnectedCallback);
        new Thread(mWriteSocketClient).start();
        new Thread(mReadSocketClient).start();
        mConnectedCallback.connectSuccess("服务器连接成功");
    }

    /**
     * 关闭socket
     */
    public void stopClient() {
        stopRead();
        stopWrite();
        stopSocket();
    }

    /**
     * 关闭读线程
     */
    private void stopRead() {
        if (mReadSocketClient != null) {
            mReadSocketClient.stop();
            mReadSocketClient = null;
        }
    }

    /**
     * 关闭写线程
     */
    private void stopWrite() {
        if (mWriteSocketClient != null) {
            mWriteSocketClient.stop();
            mWriteSocketClient = null;
        }
    }

    /**
     * 关闭socket
     */
    private void stopSocket() {
        if (mSocket != null) {
            if (mSocket.isConnected()) {
                try {
                    mSocket.shutdownInput();
                    mSocket.shutdownOutput();
                    mSocket.close();
                } catch (IOException e) {

                }
            }
            mSocket = null;
        }
    }

    /**
     * 通过写线程发送数据
     */
    public void sendData(ClientPacket packet) {
        mWriteSocketClient.send(packet);
    }
}